//AUTHOR: Ravi 

package com.redribbon.droidnoise;

import java.io.*;
import java.util.HashMap;

import android.content.Context;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

// These functions receive various types of data, put it into a byte array and send
// it to the networking server

abstract class Message implements Serializable
{
	byte type;
	abstract void doActionOnReceive();
}

class MediaDataMessage extends Message
{
	long bytePosition;
	byte arr[];	//Ideally InputStream
	
	MediaDataMessage()	
	{
		type = NoiseConstants.MEDIADATAMESSAGETYPE;
	}
	
	void doActionOnReceive()
	{
		//By now, bytePosition and arr have been filled
		Log.i("RAVI INFO", "Received Media Message");
	}
}

class MetaDataMessage extends Message
{
	public String artist;
	public String title;
	public String album;
	public Long duration;
	public Long size;
	public String StationName;
	
	MetaDataMessage()
	{
		type = NoiseConstants.METADATAMESSAGETYPE;
	}
	
	void doActionOnReceive()
	{
		//artist, title, album, duration, size have now been filled
		Log.i("RAVI INFO", "Received Metadata Message");
	}
}




class MessageParser implements Runnable
{
	ByteArrayOutputStream sendBuffer;
	MediaDataMessage sendMediaMsg, recvMediaMsg;
	MetaDataMessage  sendMetaMsg,  recvMetaMsg;
	Message recvMsg;
	ObjectOutputStream oos;
	NoiseServer droidServer;
	NoiseClient droidClient;
	MediaStreamer mediaStreamer;
	
	int count = 0;
	
	RemoteCallbackList<StreamClientServiceCallback> mCallbacks;
	

	private static final int BUMP_MSG = 1;
	   
	
	MessageParser(NoiseServer droidNet) 		// Constructor to be called on Server side
	{
		this.droidServer = droidNet;
		sendBuffer = new ByteArrayOutputStream(NoiseConstants.BUFFERSIZE);
		sendMediaMsg = new MediaDataMessage();
		sendMetaMsg = new MetaDataMessage();
		try
		{
			oos = new ObjectOutputStream(sendBuffer);
		}
		catch(IOException ioe)
		{
			Log.e("RAVI INFO", "MessageParser constructer. IOException");
		}
	}
	
	MessageParser(Context ourContext,  RemoteCallbackList<StreamClientServiceCallback> cb)	//Constructor to be called on Client side  
	{
		try
		{
			mediaStreamer = new MediaStreamer(ourContext);	//Or should I instantiate these?
			droidClient = new NoiseClient();
			mCallbacks = cb;
			//readMessage();
		}
		catch (Exception e)
		{	Log.e("RAVI INFO", "MessageParser Client constructor exception.");	
		}
		
	}

	// Reads Media data from filetoRead and sends to NoiseServer
	//void sendMediaData(ByteArrayInputStream arrayToRead, int length, long timePoint)
	void sendMediaData(FileInputStream fileToRead, int length, long timePoint)
	{
//		Log.i("RAVI INFO", "opened sendMediaData with "+length+" bytes");
		sendMediaMsg.bytePosition = timePoint;
		sendMediaMsg.arr = new byte[length];
		//arrayToRead.read(sendMediaMsg.arr, 0, length);
		try
		{
			int bytesRead = fileToRead.read(sendMediaMsg.arr, 0, length);
//			Log.i("RAVI INFO", "Read " +bytesRead+" bytes" );
//			Log.i("RAVI INFO", "Sending "+ sendBuffer.size()+" bytes");
//			Log.i("RAVI INFO", "Before Packing "+ sendBuffer.size()+" bytes");

			oos.writeObject(sendMediaMsg);				//This tunnels the data into the buffer object
			Log.i("RAVI INFO", "Sending "+ bytesRead+" bytes");
			//droidServer.sendData(sendBuffer.toByteArray(), sendBuffer.size());	//MIGHT BE VERY INEFFICIENT
			droidServer.sendData(sendMediaMsg.arr, bytesRead);	//MIGHT BE VERY INEFFICIENT
			sendBuffer.reset();
			if(count++ > NoiseConstants.METADATAFREQUENCY)
			{
				count = 0;
				sendMetaData();
			}
		}
		catch(IOException ioe)
		{
			
		}
	}
	
	// Changes the metadata that needs to be sent periodically 
	void changeMetaData(SongContainer metaData)	
	{
		sendMetaMsg.artist = metaData.artist;
		sendMetaMsg.title = metaData.title;
		sendMetaMsg.album = metaData.album;
		sendMetaMsg.duration = metaData.duration;
		sendMetaMsg.size = metaData.size;
		sendMetaMsg.StationName = metaData.ServerStationName;		
	}
	//Sends stored metadata to NoiseServer
	void sendMetaData()	throws IOException
	{		
		oos.writeObject(sendMetaMsg); 
		droidServer.sendData(sendBuffer.toByteArray(), sendBuffer.size());	//MIGHT BE VERY INEFFICIENT
		sendBuffer.reset();
	}
	
	
	//Reads from byte array and reconstructs the Message 
	void readMessage() throws IOException, ClassNotFoundException, InterruptedException
	{
		while(1==1)
		{
			
			byte array[] = droidClient.getNoise();
			if(array == null)
			{
				Thread.sleep(500);
				continue;
			}
			ByteArrayInputStream recvBuffer = new ByteArrayInputStream (array, 0, array.length);
			mediaStreamer.startStreaming(recvBuffer);

			
			
//			int len = array.length;
//			Log.i("RAVI INFO", "Trying to parse "+len+" bytes");
//			ByteArrayInputStream recvBuffer = new ByteArrayInputStream (array, 0, len);
//			ObjectInputStream ois = new ObjectInputStream(recvBuffer);
//			recvMsg = (Message) ois.readObject();
//			
//			recvMsg.doActionOnReceive();	//Would this call the appropriate overriden function?
//			
//			if(recvMsg.type == NoiseConstants.MEDIADATAMESSAGETYPE)
//			{
//				MediaDataMessage mediaMsg = (MediaDataMessage) recvMsg;
//				ByteArrayInputStream inputstream = new ByteArrayInputStream(mediaMsg.arr);
//				mediaStreamer.startStreaming(inputstream);			
//			}
//			if(recvMsg.type == NoiseConstants.METADATAMESSAGETYPE)
//			{
//				MetaDataMessage metaMsg = (MetaDataMessage) recvMsg;
//				//Display data in UI
//				SongContainer showInUI = new SongContainer(metaMsg.title, metaMsg.artist, metaMsg.album, metaMsg.duration, metaMsg.size, null);
//				showInUI.ServerStationName = metaMsg.StationName;
//				//Assign them to constants
//				try {
//					NoiseConstants.current_playing_station = showInUI.ServerStationName;
//					NoiseConstants.current_playing_track = showInUI.title;
//					NoiseConstants.current_playing_artist = showInUI.artist;
//					NoiseConstants.current_playing_album = showInUI.album;
//					final int N = mCallbacks.beginBroadcast();
//					for (int i = 0; i < N; i++)
//						mCallbacks.getBroadcastItem(i).valueChanged(BUMP_MSG);				
//					mCallbacks.finishBroadcast();
//				} catch (Exception e) {
//					
//				}
//			}
			
			Thread.sleep(500);	//Poll for message regularly
		}
		
	}

	public void run() {
		try {
			readMessage();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}	
	
}